/********************************************************************

Write Date: 2011/06/02
Author: Deng Nianchen
>>>> Implement OOPObject

Modify Date: 2011/07/16
Author: Deng Nianchen
>>>> Fix bugs on handling operator new[]
>>>> Add comments to class OOPObjectCreateInfo
>>>> Implement copy constructor and assignment operator

*********************************************************************/
#include "OOPObject.h"
#include <Windows.h>
#include <map>
#include <stack>
#include <iostream>

using namespace OOPUtils;
using namespace std;

/**************************************************************************************************
 * <summary> Purpose: OOPObjectCreateInfo is a helper class for passing the
 *					 information of new operator to the constructor of
 *					 corresponding OOPObject object.
 * 					 Thread-Safe: the class is thread-safe. </summary>
 *
 * <remarks>  Deng Nianchen, 2011/07/16. </remarks>
 **************************************************************************************************/

class OOPObjectCreateInfo
{
public:
	/* start address */
	void * address;

	/* is allocated by new[] operator? */
	bool isArray;

	/* allocation size in byte */
	size_t sizeInByte;

	size_t arraySize;

	size_t elemSize;

	size_t curInitializingIndex;

	OOPObjectCreateInfo ();

	static void lock ();
	static void unlock ();
	static OOPObjectCreateInfo *newThreadLocalInfo ();
	static OOPObjectCreateInfo *getThreadLocalInfo ();
	static void releaseThreadLocalInfo ();

private:
	static map< DWORD, stack< OOPObjectCreateInfo > > _infoStackForEachThread;
	static HANDLE _hMapProtectMutex;
	static pair< DWORD, stack< OOPObjectCreateInfo > * > _infoStackPtrCache;
};

HANDLE OOPObjectCreateInfo::_hMapProtectMutex = CreateMutex (NULL, FALSE, NULL);

pair< DWORD, stack< OOPObjectCreateInfo > * > OOPObjectCreateInfo::_infoStackPtrCache =
		make_pair< DWORD, stack< OOPObjectCreateInfo > * > (NULL, NULL);

map< DWORD, stack< OOPObjectCreateInfo > > OOPObjectCreateInfo::_infoStackForEachThread;

OOPObjectCreateInfo::OOPObjectCreateInfo ()
		: address (0), isArray (false), sizeInByte (0), arraySize (-1),
		elemSize (0), curInitializingIndex (0)
{

}

void OOPObjectCreateInfo::lock ()
{
	WaitForSingleObject (_hMapProtectMutex, INFINITE);
}

void OOPObjectCreateInfo::unlock ()
{
	ReleaseMutex (_hMapProtectMutex);
}

OOPObjectCreateInfo *OOPObjectCreateInfo::newThreadLocalInfo ()
{
	DWORD hCurThread = GetCurrentThreadId ();
	stack< OOPObjectCreateInfo > *pInfoStack = NULL;
	lock ();
	if (hCurThread == _infoStackPtrCache.first)
		pInfoStack = _infoStackPtrCache.second;
	else {
		_infoStackPtrCache.second = pInfoStack =
				&_infoStackForEachThread[hCurThread];
		_infoStackPtrCache.first = hCurThread;
	}
	unlock ();
	pInfoStack->push (OOPObjectCreateInfo ());
	return &pInfoStack->top ();
}

OOPObjectCreateInfo *OOPObjectCreateInfo::getThreadLocalInfo ()
{
	DWORD hCurThread = GetCurrentThreadId ();
	stack< OOPObjectCreateInfo > *pInfoStack = NULL;
	lock ();
	if (hCurThread == _infoStackPtrCache.first)
		pInfoStack = _infoStackPtrCache.second;
	else {
		map< DWORD, stack< OOPObjectCreateInfo > >::iterator iter =
				_infoStackForEachThread.find (hCurThread);
		if (iter == _infoStackForEachThread.end ()) {
			unlock ();
			return NULL;
		}
		_infoStackPtrCache.second = pInfoStack = &iter->second;
		_infoStackPtrCache.first = hCurThread;
	}
	unlock ();
	if (pInfoStack->empty ())
		return NULL;
	return &pInfoStack->top ();
}

void OOPObjectCreateInfo::releaseThreadLocalInfo ()
{
	DWORD hCurThread = GetCurrentThreadId ();
	stack< OOPObjectCreateInfo > *pInfoStack = NULL;
	lock ();
	if (hCurThread == _infoStackPtrCache.first)
		pInfoStack = _infoStackPtrCache.second;
	else {
		map< DWORD, stack< OOPObjectCreateInfo > >::iterator iter =
			_infoStackForEachThread.find (hCurThread);
		if (iter == _infoStackForEachThread.end ()) {
			unlock ();
			return;
		}
		_infoStackPtrCache.second = pInfoStack = &iter->second;
		_infoStackPtrCache.first = hCurThread;
	}
	unlock ();
	pInfoStack->pop ();
}

OOPObject::OOPObject () : _allocInfo (ON_STACK), _refNum ()
{
	_pRefAffectObj = this;
	OOPObjectCreateInfo *pCreationInfo =
			OOPObjectCreateInfo::getThreadLocalInfo ();
	if (pCreationInfo != NULL) {
		int distance;
		if ((char*)this < (char*)pCreationInfo->address)
			distance = (char*)pCreationInfo->address - (char*)this;
		else
			distance = (char*)this - (char*)pCreationInfo->address;
		if (pCreationInfo->isArray) {
			size_t index = distance / pCreationInfo->elemSize;
			if (index == pCreationInfo->curInitializingIndex) {
				_allocInfo = ARRAY_ON_HEAP;
				_pRefAffectObj = (OOPObject *)pCreationInfo->address;
				if (index == pCreationInfo->arraySize - 1)
					OOPObjectCreateInfo::releaseThreadLocalInfo ();
				++pCreationInfo->curInitializingIndex;
			}
		} else if (distance == 0) {
			_allocInfo = ON_HEAP;
			OOPObjectCreateInfo::releaseThreadLocalInfo ();
		}
	}

// 	cout << "Create a oopobject at address " << hex << (int)this;
// 	if (isArrOnHeap ())
// 		cout << "(Array address: " << hex << (int)getRefAffectObj () << ")";
// 	else if (isOnHeap ())
// 		cout << "(Heap)";
// 	else
// 		cout << "(Stack)";
// 	cout << endl;
}

OOPObject::OOPObject (const OOPObject &obj) : _allocInfo (ON_STACK), _refNum ()
{
	_pRefAffectObj = this;
	OOPObjectCreateInfo *pCreationInfo =
			OOPObjectCreateInfo::getThreadLocalInfo ();
	if (pCreationInfo != NULL) {
		int distance;
		if ((char*)this < (char*)pCreationInfo->address)
			distance = (char*)pCreationInfo->address - (char*)this;
		else
			distance = (char*)this - (char*)pCreationInfo->address;
		if (distance == 0) {
			_allocInfo = ON_HEAP;
			OOPObjectCreateInfo::releaseThreadLocalInfo ();
		}
	}
}

OOPObject &OOPObject::operator= (const OOPObject &right)
{
	return *this;
}

OOPObject::~OOPObject ()
{
// 	cout << "Destroy a oopobject at address " << hex << (int)this;
// 	if (isArrOnHeap ())
// 		cout << "(Array address: " << hex << (int)getRefAffectObj () << ")";
// 	else if (isOnHeap ())
// 		cout << "(Heap)";
// 	else
// 		cout << "(Stack)";
// 	cout << endl;
}

void *OOPObject::operator new (size_t size)
{
	void *p = ::operator new (size);
	if (p == NULL)
		return p;
	OOPObjectCreateInfo *pNewCreationInfo = NULL;
	OOPObjectCreateInfo::lock ();
	pNewCreationInfo = OOPObjectCreateInfo::newThreadLocalInfo ();
	OOPObjectCreateInfo::unlock ();
	pNewCreationInfo->address = p;
	pNewCreationInfo->isArray = false;
	pNewCreationInfo->sizeInByte = size;
	pNewCreationInfo->arraySize = -1;
	pNewCreationInfo->elemSize = size;
	return p;
}

void *OOPObject::operator new[] (size_t size)
{
	return NULL;
}

void *OOPObject::operator new[] (size_t size, size_t elemNum, size_t elemSize)
{
	void *p = ::operator new[] (size);
	if (p == NULL)
		return p;
	OOPObjectCreateInfo *pNewCreationInfo = NULL;
	OOPObjectCreateInfo::lock ();
	pNewCreationInfo = OOPObjectCreateInfo::newThreadLocalInfo ();
	OOPObjectCreateInfo::unlock ();
	pNewCreationInfo->address = (char *)p + 4;
	pNewCreationInfo->isArray = true;
	pNewCreationInfo->sizeInByte = size;
	pNewCreationInfo->arraySize = elemNum;
	pNewCreationInfo->elemSize = elemSize;
	return p;
}

void OOPObject::operator delete[] (void *ptr, size_t elemNum, size_t elemSize)
{
	// Exception thrown when initializing the array
	// The creationInfo is useless
	OOPObjectCreateInfo::releaseThreadLocalInfo ();
	::operator delete[](ptr);
}

void OOPObject::operator delete[] (void *ptr)
{
	::operator delete[](ptr);
}

bool OOPObject::isOnHeap () const
{
	return _allocInfo == ON_HEAP;
}

bool OOPObject::isArrOnHeap () const
{
	return _allocInfo == ARRAY_ON_HEAP;
}

int OOPObject::getRefNum () const
{
	return _refNum;
}

const OOPObject * OOPUtils::OOPObject::getRefAffectObj () const
{
	return _pRefAffectObj;
}